package com.zhouyu.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: shouxieSpring
 * @author: jzhou
 * @date: 2022-04-13 22:11
 * @version: 1.0
 * @description:
 **/
public class ZhouyuApplicationContext {
    private Class configClass;
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    /** 存放单例bean */
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();

    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public ZhouyuApplicationContext(Class configClass) {
        this.configClass = configClass;
        // 扫描 --》 得到很多的beanDefinition对象
        if(configClass.isAnnotationPresent(ComponentScan.class)){
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();
            path = path.replace(".","/");
            ClassLoader classLoader = ZhouyuApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());
//            System.out.println(file);
            if (file.isDirectory()){
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();
//                    System.out.println(fileName);
                    if (fileName.endsWith(".class")) {
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\",".");
//                        System.out.println(className);
                        try {
                            Class<?> aClass = classLoader.loadClass(className);
                            if (aClass.isAnnotationPresent(Component.class)){

                                // 表示类是否由指定接口派生的
                                if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                                    BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.newInstance();
                                    beanPostProcessorList.add(beanPostProcessor);
                                }

                                Component annotation = aClass.getAnnotation(Component.class);
                                String beanName = annotation.value();

                                if ("".equals(beanName)){
                                    beanName = Introspector.decapitalize(aClass.getSimpleName());
                                }

                                BeanDefinition beanDefinition = new BeanDefinition();
                                if (aClass.isAnnotationPresent(Scope.class)){
                                    Scope scope = aClass.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                }else {
                                    beanDefinition.setScope("singleton");
                                }
                                beanDefinition.setType(aClass);
                                beanDefinitionMap.put(beanName,beanDefinition);

                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }

        // 实例化单例对象
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition) {

        Class clazz = beanDefinition.getType();
        try {

            Object instance = clazz.getConstructor().newInstance();

            // 依赖注入
            for (Field declaredField : clazz.getDeclaredFields()) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    declaredField.setAccessible(true);
                    declaredField.set(instance,getBean(declaredField.getName()));
                }
            }

            // Aware 回调
            if(instance instanceof BeanNameAware){
                ( (BeanNameAware) instance).setBeanName(beanName);
            }

            // 初始化 前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance =  beanPostProcessor.postProcessBeforeInitialization(beanName,instance);
            }

            // 初始化
            if(instance instanceof InitiallzingBean){
                ( (InitiallzingBean) instance).afterPropertiesSet();
            }

            // 初始化后AOP  beanPostProcessor  翻译成bean的后置处理器
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName,instance);
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Object getBean(String beanName){

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition == null){
            throw new NullPointerException();
        }else {
            String scope = beanDefinition.getScope();
            if ("singleton".equals(scope)){
                Object bean = singletonObjects.get(beanName);
                if (bean == null) {
                    Object bean1 = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName,bean1);
                }
                return bean;
            }else {
                return createBean(beanName,beanDefinition);
            }
        }
    }
}
